home *** CD-ROM | disk | FTP | other *** search
/ Macwelt 4 / Macwelt DVD 4.cdr / Entwickler / Mac-OS X / Pantomime / Source / Part.m / Part.m
Encoding:
Text File  |  2002-08-08  |  16.1 KB  |  738 lines

  1. /*
  2. **  Part.m
  3. **
  4. **  Copyright (c) 2001, 2002
  5. **
  6. **  Author: Ludovic Marcotte <ludovic@Sophos.ca>
  7. **
  8. **  This library is free software; you can redistribute it and/or
  9. **  modify it under the terms of the GNU Lesser General Public
  10. **  License as published by the Free Software Foundation; either
  11. **  version 2.1 of the License, or (at your option) any later version.
  12. **  
  13. **  This library is distributed in the hope that it will be useful,
  14. **  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. **  Lesser General Public License for more details.
  17. **  
  18. **  You should have received a copy of the GNU Lesser General Public
  19. **  License along with this library; if not, write to the Free Software
  20. **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. */
  22.  
  23. #import <Pantomime/Part.h>
  24.  
  25. #import <Pantomime/Constants.h>
  26. #import <Pantomime/NSStringExtensions.h>
  27. #import <Pantomime/NSDataExtensions.h>
  28. #import <Pantomime/MimeUtility.h>
  29. #import <Pantomime/MimeBodyPart.h>
  30. #import <Pantomime/MimeMultipart.h>
  31. #import <Pantomime/Message.h>
  32.  
  33. static int currentVersion = 1;
  34.  
  35. @implementation Part
  36.  
  37. //
  38. //
  39. //
  40. - (id) init
  41. {
  42.   self = [super init];
  43.   
  44.   [Part setVersion: currentVersion];
  45.  
  46.   // We set the default values
  47.   [self setContentType: @"text/plain"];
  48.   [self setContentTransferEncoding: NONE];
  49.   [self setCharset: @"us-ascii"];
  50.   [self setFormat: FORMAT_UNKNOWN];
  51.   [self setLineLength: 0];
  52.   
  53.   return self;
  54. }
  55.  
  56.  
  57. //
  58. //
  59. //
  60. - (void) dealloc
  61. {
  62.   //NSDebugLog(@"Part: -dealloc");
  63.   
  64.   TEST_RELEASE(content);
  65.  
  66.   RELEASE(contentType);
  67.   RELEASE(contentID);
  68.   RELEASE(contentDescription);
  69.   RELEASE(contentDisposition);
  70.   RELEASE(filename);
  71.   RELEASE(boundary);
  72.   RELEASE(protocol);
  73.   RELEASE(charset);
  74.   
  75.   TEST_RELEASE(defaultCharset);
  76.  
  77.   [super dealloc];
  78. }
  79.  
  80.  
  81. //
  82. //
  83. //
  84. - (id) initWithData: (NSData *) theData
  85. {
  86.   [Part setVersion: currentVersion];
  87.   
  88.   [self subclassResponsibility: _cmd];
  89.   return nil;
  90. }
  91.  
  92.  
  93. //
  94. //
  95. //
  96. - (id) initWithData: (NSData *) theData
  97.             charset: (NSString *) theCharset
  98. {
  99.   [Part setVersion: currentVersion];
  100.   
  101.   [self subclassResponsibility: _cmd];
  102.   return nil;
  103. }
  104.  
  105.  
  106. //
  107. // NSCoding protocol
  108. //
  109. - (void) encodeWithCoder: (NSCoder *) theCoder
  110. {
  111.   [Part setVersion: currentVersion];
  112.  
  113.   [theCoder encodeObject: [self contentType]];
  114.   [theCoder encodeObject: [self contentID]];
  115.   [theCoder encodeObject: [self contentDescription]];
  116.   [theCoder encodeObject: [self contentDisposition]];
  117.   [theCoder encodeObject: [self filename]];
  118.  
  119.   [theCoder encodeObject: [NSNumber numberWithInt: [self contentTransferEncoding]]];
  120.   [theCoder encodeObject: [NSNumber numberWithInt: [self format]]];
  121.   [theCoder encodeObject: [NSNumber numberWithInt: [self size]]];
  122.  
  123.   [theCoder encodeObject: [self boundary]];
  124.   [theCoder encodeObject: [self charset]];
  125. }
  126.  
  127. - (id) initWithCoder: (NSCoder *) theCoder
  128. {
  129.   int version;
  130.  
  131.   version = [theCoder versionForClassName: NSStringFromClass([self class])];
  132.   
  133.   self = [super init];
  134.  
  135.   [self setContentType: [theCoder decodeObject]];
  136.   [self setContentID: [theCoder decodeObject]];
  137.   [self setContentDescription: [theCoder decodeObject]];
  138.   [self setContentDisposition: [theCoder decodeObject]];
  139.   [self setFilename: [theCoder decodeObject]];
  140.  
  141.   [self setContentTransferEncoding: [[theCoder decodeObject] intValue]];
  142.   [self setFormat: [[theCoder decodeObject] intValue]];
  143.   [self setSize: [[theCoder decodeObject] intValue]];
  144.  
  145.   // Old version, we were using a NSString object
  146.   if ( version == 1 )
  147.     {
  148.       [self setBoundary: [theCoder decodeObject]];
  149.     }
  150.   // We now use a NSData object
  151.   else
  152.     {
  153.       id obj;
  154.  
  155.       obj = [theCoder decodeObject];
  156.  
  157.       if ( [obj isKindOfClass: [NSString class]] )
  158.     {
  159.       [self setBoundary: [obj dataUsingEncoding: NSASCIIStringEncoding]];
  160.     }
  161.       else
  162.     {
  163.       [self setBoundary: obj];
  164.     }
  165.     }
  166.   
  167.   [self setCharset: [theCoder decodeObject]];
  168.   [self setDefaultCharset: nil];
  169.  
  170.   return self;
  171. }
  172.  
  173.  
  174.  
  175. //
  176. // access / mutation methods
  177. //
  178.  
  179. //
  180. //
  181. //
  182. - (NSObject *) content
  183. {
  184.   return content;
  185. }
  186.  
  187.  
  188. //
  189. //
  190. //
  191. - (void) setContent: (NSObject *) theContent
  192. {
  193.   RETAIN(theContent);
  194.   RELEASE(content);
  195.   content = theContent;
  196. }
  197.  
  198. - (NSString *) contentType
  199. {
  200.   return contentType;
  201. }
  202.  
  203. - (void) setContentType: (NSString*) theContentType
  204. {  
  205.   RETAIN(theContentType);
  206.   RELEASE(contentType);
  207.   contentType = theContentType;
  208. }
  209.  
  210.  
  211. - (NSString *) contentID
  212. {
  213.   return contentID;
  214. }
  215.  
  216. - (void) setContentID: (NSString *) theContentID
  217. {
  218.   RETAIN(theContentID);
  219.   RELEASE(contentID);
  220.   contentID = theContentID;
  221. }
  222.  
  223.  
  224. - (NSString *) contentDescription
  225. {
  226.   return contentDescription;
  227. }
  228.  
  229. - (void) setContentDescription: (NSString*)theContentDescription
  230. {
  231.   RETAIN(theContentDescription);
  232.   RELEASE(contentDescription);
  233.   contentDescription = theContentDescription;
  234. }
  235.  
  236. - (NSString *) contentDisposition
  237. {
  238.   return contentDisposition;
  239. }
  240.  
  241. - (void) setContentDisposition: (NSString *) theContentDisposition
  242. {
  243.   RETAIN(theContentDisposition);
  244.   RELEASE(contentDisposition);
  245.   contentDisposition = theContentDisposition;
  246. }
  247.  
  248. - (int) contentTransferEncoding
  249. {
  250.   return contentTransferEncoding;
  251. }
  252.  
  253. - (void) setContentTransferEncoding: (int) theEncoding
  254. {
  255.   contentTransferEncoding = theEncoding;
  256. }
  257.  
  258. - (NSString *) filename
  259. {
  260.   return filename;
  261. }
  262.  
  263. - (void) setFilename: (NSString *) theFilename
  264. {
  265.   if ( theFilename && ([theFilename length] > 0) )
  266.     {
  267.       RETAIN(theFilename);
  268.       RELEASE(filename);
  269.       filename = theFilename;
  270.     }
  271.   else
  272.     {
  273.       RELEASE(filename);
  274.       filename = @"unknown";
  275.       RETAIN(filename);
  276.     }
  277. }
  278.  
  279.  
  280. - (int) format
  281. {
  282.   return format;
  283. }
  284.  
  285. - (void) setFormat: (int) theFormat
  286. {
  287.   format = theFormat;
  288. }
  289.  
  290. - (int) lineLength
  291. {
  292.   return line_length;
  293. }
  294.  
  295. - (void) setLineLength: (int) theLineLength
  296. {
  297.   line_length = theLineLength;
  298. }
  299.  
  300.  
  301. //
  302. // This method is used to very if the part is of the following primaryType / subType
  303. //
  304. - (BOOL) isMimeType: (NSString *) primaryType : (NSString *) subType 
  305. {
  306.   NSString *str;
  307.  
  308.   /* If the message has not content-type, we treat it as text/plain by adding it*/
  309.   if (! [self contentType] ) 
  310.     {
  311.       [self setContentType: @"text/plain"];
  312.     }
  313.  
  314.   if ( [subType compare: @"*"] == NSOrderedSame )
  315.     {
  316.       str = [self contentType];
  317.       
  318.       if ( [[self contentType] hasCaseInsensitivePrefix: primaryType] )
  319.     {
  320.       return YES;
  321.     }
  322.     }
  323.   else
  324.     {
  325.       str = [NSString stringWithFormat:@"%@/%@", primaryType, subType];
  326.       if ( [str caseInsensitiveCompare:[self contentType]] == NSOrderedSame) return YES;
  327.     }
  328.   return NO;
  329. }
  330.  
  331.  
  332. - (long) size
  333. {
  334.   return size;
  335. }
  336.  
  337. - (void) setSize: (long) theSize
  338. {
  339.   size = theSize;
  340. }
  341.  
  342.  
  343. //
  344. //
  345. //
  346. - (NSData *) dataUsingSendingMode : (int) theMode
  347. {
  348.   NSMutableData *aMutableData;
  349.   NSData *aDataToSend;
  350.   
  351.   NSArray *anArray;
  352.   int i;
  353.  
  354.   char *lineTerminator;
  355.  
  356.   if ( theMode == SEND_USING_SMTP )
  357.     {
  358.       lineTerminator = CRLF;
  359.     }
  360.   else
  361.     {
  362.       lineTerminator = LF;
  363.     }
  364.  
  365.   aMutableData = [[NSMutableData alloc] init];
  366.  
  367.   // FIXME: Why is this acting differently depending on the content-type?
  368.   // easier to just encode according to content-transfer-encoding:, split to
  369.   // lines and add to the output
  370.   if ( [self contentTransferEncoding] != NONE )
  371.     {
  372.       [aMutableData appendCFormat: @"Content-Transfer-Encoding: %@%s",
  373.             [MimeUtility stringValueOfTransferEncoding: [self contentTransferEncoding]],
  374.             lineTerminator];
  375.     }
  376.  
  377.   if ( [self contentID] )
  378.     {
  379.       [aMutableData appendCFormat: @"Content-ID: %@%s", [self contentID], lineTerminator];
  380.     }
  381.   
  382.   if ( [self contentDescription] )
  383.     {
  384.       [aMutableData appendCString: "Content-Description: "];
  385.       [aMutableData appendData: [MimeUtility encodeWordUsingQuotedPrintable: [self contentDescription]
  386.                                     prefixLength: 21]];
  387.       [aMutableData appendCString: lineTerminator];
  388.     }
  389.   
  390.   if ( [self isMimeType: @"multipart" : @"*"] )
  391.     {
  392.       if ([[self content] isKindOfClass: [MimeMultipart class]])
  393.     {
  394.       MimeMultipart *mp;
  395.       MimeBodyPart *b;
  396.       NSData *aBoundary;
  397.       NSMutableData *md;
  398.  
  399.       md = [[NSMutableData alloc] init];
  400.       
  401.       aBoundary = [self boundary];
  402.       
  403.       if ( !aBoundary )
  404.         {
  405.           aBoundary = [MimeUtility generateBoundary];
  406.         }
  407.  
  408.       [aMutableData appendCFormat: @"Content-Type: %@;%s", [self contentType], lineTerminator];
  409.       
  410.       if ( [self protocol] )
  411.         {
  412.           [aMutableData appendCString: "\tprotocol=\""];
  413.           [aMutableData appendData: [self protocol]];
  414.           [aMutableData appendCFormat: @"\";%s", lineTerminator];
  415.         }
  416.       
  417.       [aMutableData appendCString: "\tboundary=\""];
  418.       [aMutableData appendData: aBoundary];
  419.       [aMutableData appendCFormat: @"\"%s", lineTerminator];
  420.       
  421.       mp = (MimeMultipart *)[self content];
  422.       
  423.       for (i = 0; i < [mp count]; i++)
  424.         {
  425.           b = [mp bodyPartAtIndex: i];
  426.           
  427.           if (i > 0)
  428.         {
  429.           [md appendBytes: lineTerminator  length: strlen(lineTerminator)];
  430.         }
  431.           
  432.           [md appendBytes: "--"  length: 2];
  433.           [md appendData: aBoundary];
  434.           [md appendBytes: lineTerminator  length: strlen(lineTerminator)];
  435.           
  436.           [md appendData: [b dataUsingSendingMode: theMode] ];
  437.         }
  438.       
  439.       [md appendBytes: "--"  length: 2];
  440.       [md appendData: aBoundary];
  441.       [md appendBytes: "--"  length: 2];
  442.       [md appendBytes: lineTerminator  length: strlen(lineTerminator)];
  443.       
  444.       // FIXME - is that ok to autorelease it?
  445.       // We never release aDataToSend anyway...
  446.       aDataToSend = AUTORELEASE(md);
  447.     }
  448.       else
  449.     {
  450.       aDataToSend = (NSData *)[self content];
  451.     }
  452.     }
  453.   else if ( [self isMimeType: @"text": @"*"] ||
  454.         [self isMimeType: @"message" : @"delivery-status"] )
  455.     { 
  456.       if ( [self isMimeType: @"text": @"plain"] && [self format] == FORMAT_FLOWED &&
  457.        ([self contentTransferEncoding] == NONE || [self contentTransferEncoding] == EIGHTBIT) )
  458.     {
  459.       [aMutableData appendCFormat: @"Content-Type: %@; charset=\"%@\"; format=\"flowed\"%s",
  460.             [self contentType], [self charset], lineTerminator];
  461.     }
  462.       else
  463.     {
  464.       // FIXME: check if charset= is a valid parameter for message/deliviry-status
  465.       [aMutableData appendCFormat: @"Content-Type: %@; charset=\"%@\"%s",
  466.             [self contentType], [self charset], lineTerminator];
  467.     }
  468.       
  469.       if ( [[self content] isKindOfClass: [NSString class]] )
  470.     {
  471.       NSString *aString;
  472.       int encoding;
  473.       
  474.       // FIXME - Should we do this if the content is a NSData or something else?
  475.       if ( ([self contentTransferEncoding] == NONE || [self contentTransferEncoding] == EIGHTBIT) &&
  476.            [self format] == FORMAT_FLOWED )
  477.         {
  478.           int limit;
  479.           
  480.           limit = [self lineLength];
  481.           
  482.           if (limit < 2 || limit > 998)
  483.         {
  484.           limit = 72;
  485.         }
  486.           
  487.           aString = [MimeUtility unwrapPlainTextString: (NSString *)[self content]
  488.                      usingQuoteWrappingLimit: 998];
  489.           aString = [MimeUtility wrapPlainTextString: aString
  490.                      usingWrappingLimit: limit];
  491.         }
  492.       else
  493.         {
  494.           aString = (NSString *)[self content];
  495.         }
  496.  
  497.       // We get the right string encoding to convert the string object to a data object 
  498.       encoding = [MimeUtility stringEncodingForCharset: [[self charset] dataUsingEncoding: NSASCIIStringEncoding]];
  499.       
  500.       // FIXME - Should we allow lossy conversion?
  501.       aDataToSend = [aString dataUsingEncoding: encoding
  502.                  allowLossyConversion: YES];
  503.       
  504.     }
  505.       else if ([[self content] isKindOfClass: [NSData class]])
  506.     {
  507.       aDataToSend = (NSData *)[self content];
  508.     }
  509.       // If it isn't string or data it had better respond to this
  510.       else 
  511.     {
  512.       aDataToSend = [(Part *)[self content] dataUsingSendingMode: theMode];
  513.     }
  514.       
  515.       //
  516.       // If we had a Content-Disposition specified, let's add it.
  517.       //
  518.       if ( [self contentDisposition] )
  519.     {
  520.       // If it is an 'attachment', let's add the filename.
  521.       if ( [[self contentDisposition] caseInsensitiveCompare: @"attachment"] == NSOrderedSame &&
  522.            [self filename] )
  523.         {
  524.           NSString *aString;
  525.           
  526.           if ( [MimeUtility isASCIIString: [self filename]] )
  527.         {
  528.           aString = [self filename];
  529.         }
  530.           else
  531.         {
  532.           aString = [[NSString alloc] initWithData: [MimeUtility encodeWordUsingQuotedPrintable: [self filename]
  533.                                      prefixLength: 0]
  534.                           encoding: NSASCIIStringEncoding];
  535.           AUTORELEASE(aString);
  536.         }
  537.           
  538.           [aMutableData appendCFormat: @"Content-Disposition: attachment; filename=\"%@\"%s",
  539.                 aString, lineTerminator];
  540.         }
  541.       else
  542.         {
  543.           [aMutableData appendCFormat: @"Content-Disposition: %@%s", [self contentDisposition], lineTerminator];
  544.         }
  545.     }
  546.     }
  547.   //
  548.   // Our Content-Type is message/rfc822 or message/partial
  549.   //
  550.   else if ( [self isMimeType: @"message": @"rfc822"] ||
  551.         [self isMimeType: @"message": @"partial"] )
  552.     {
  553.       [aMutableData appendCFormat: @"Content-Type: %@%s",  [self contentType], lineTerminator];
  554.       
  555.       //
  556.       // If we had a Content-Disposition specified, let's add it.
  557.       //
  558.       if ( [self contentDisposition] )
  559.     {
  560.       [aMutableData appendCFormat:@"Content-Disposition: %@%s", [self contentDisposition], lineTerminator];
  561.     }
  562.  
  563.       if ( [[self content] isKindOfClass: [NSData class]] )
  564.     {
  565.       aDataToSend = (NSData *)[self content];
  566.     }
  567.       else
  568.     {
  569.       aDataToSend = [(Message *)[self content] rawSource];
  570.     }
  571.     }
  572.   //
  573.   // We surely have an application/*, audio/*, image/*,
  574.   // video/* or anything else. We treat everything
  575.   // like application/octet-stream
  576.   //
  577.   else
  578.     {
  579.       NSString *aString;
  580.       
  581.       if ( [MimeUtility isASCIIString: [self filename]] )
  582.     {
  583.       aString = [self filename];
  584.     }
  585.       else
  586.     {
  587.       aString = [[NSString alloc] initWithData: [MimeUtility encodeWordUsingQuotedPrintable: [self filename]
  588.                                  prefixLength: 0]
  589.                       encoding: NSASCIIStringEncoding];
  590.       AUTORELEASE(aString);
  591.     }
  592.       
  593.       if ( aString )
  594.     {
  595.       [aMutableData appendCFormat: @"Content-Type: %@; name=\"%@\"%s", [self contentType], aString,
  596.             lineTerminator];
  597.       
  598.       [aMutableData appendCFormat: @"Content-Disposition: attachment; filename=\"%@\"%s", 
  599.             aString, lineTerminator];
  600.     }
  601.       else
  602.     {
  603.       [aMutableData appendCFormat: @"Content-Type: %@%s", [self contentType], lineTerminator];
  604.       
  605.     }
  606.       
  607.       // FIXME: We could have a NSString as the content! We currently assume
  608.       // its encoding is NSASCIIStringEncoding
  609.       if ( [[self content] isKindOfClass: [NSString class]] )
  610.     {
  611.       aDataToSend = [(NSString *)[self content] dataUsingEncoding: NSASCIIStringEncoding
  612.                      allowLossyConversion: YES];
  613.     }
  614.       else
  615.     {
  616.          aDataToSend = (NSData *)[self content];
  617.     }
  618.     }
  619.  
  620.  
  621.   // We separe our part's headers from the content
  622.   [aMutableData appendCFormat: @"%s", lineTerminator];
  623.  
  624.   // We now encode our content the way it was specified
  625.   if ( [self contentTransferEncoding] == QUOTEDPRINTABLE )
  626.     {
  627.       aDataToSend = [MimeUtility encodeQuotedPrintable: aDataToSend
  628.                   lineLength: 72
  629.                   inHeader: NO];
  630.     }
  631.   else if ( [self contentTransferEncoding] == BASE64 )
  632.     {
  633.       aDataToSend = [MimeUtility encodeBase64: aDataToSend
  634.                   lineLength: 72];
  635.     }
  636.   else
  637.     {
  638.       aDataToSend = aDataToSend;
  639.     }
  640.   
  641.   //
  642.   // We now separate our content using the line feed as the delimiter
  643.   // and we rebuild it using the right delimiter.
  644.   //
  645.   anArray = [aDataToSend componentsSeparatedByCString: "\n"];
  646.   
  647.   for (i = 0; i < [anArray count]; i++)
  648.     {
  649.       if ( i == [anArray count] - 1 
  650.        && [[anArray objectAtIndex: i] length] == 0 )
  651.       {
  652.     break;
  653.       }
  654.       
  655.       [aMutableData appendData: [anArray objectAtIndex: i]];
  656.       [aMutableData appendBytes: lineTerminator  length: strlen(lineTerminator)];
  657.     }
  658.   
  659.   return AUTORELEASE(aMutableData);
  660. }
  661.  
  662.  
  663. //
  664. //
  665. //
  666. - (NSData *) boundary
  667. {
  668.   return boundary;
  669. }
  670.  
  671. - (void) setBoundary: (NSData *) theBoundary
  672. {
  673.   RETAIN(theBoundary);
  674.   RELEASE(boundary);
  675.   boundary = theBoundary;
  676. }
  677.  
  678.  
  679. //
  680. //
  681. //
  682. - (NSData *) protocol
  683. {
  684.   return protocol;
  685. }
  686.  
  687. - (void) setProtocol: (NSData *) theProtocol
  688. {
  689.   RETAIN(theProtocol);
  690.   RELEASE(protocol);
  691.   protocol = theProtocol;
  692. }
  693.  
  694.  
  695. //
  696. //
  697. //
  698. - (NSString *) charset
  699. {
  700.   return charset;
  701. }
  702.  
  703. - (void) setCharset: (NSString *) theCharset
  704. {
  705.   RETAIN(theCharset);
  706.   RELEASE(charset);
  707.   charset = theCharset;
  708. }
  709.  
  710.  
  711. //
  712. //
  713. //
  714. - (NSString *) defaultCharset
  715. {
  716.   return defaultCharset;
  717. }
  718.  
  719.  
  720. //
  721. //
  722. //
  723. - (void) setDefaultCharset: (NSString *) theCharset
  724. {
  725.   if ( theCharset )
  726.     {
  727.       RETAIN(theCharset);
  728.       RELEASE(defaultCharset);
  729.       defaultCharset = theCharset;
  730.     }
  731.   else
  732.     {
  733.       DESTROY(defaultCharset);
  734.     }
  735. }
  736.  
  737. @end
  738.